Global Child Mortality Trends

Author

John Hickey

Published

Invalid Date

Global Child Mortality Trends

and Influencing Factors

It is shocking but true.

Children are not afforded the same opportunities for survival on a global scale. Four out of five children perish before the age of five, with Sub-Saharan Africa (57 %) and South Asia (26 %) experiencing the highest rates of child mortality. In these two regions, only three out of five births are live deliveries. Wealth is also correlated with survival, as twice as many children from impoverished households die before the age of five as those from wealthy families.

Code
import polars as pl 
import pandas as pd
import plotly.express as px
import itertools

df_data       = pd.read_csv("Quarto-Assignment/unicef_data.csv",       skipinitialspace=True)
df_metadata   = pd.read_csv("Quarto-Assignment/unicef_metadata.csv",   skipinitialspace=True)
df_additional = pd.read_csv("Quarto-Assignment/unicef_additional.csv", skipinitialspace=True)



for df in (df_data, df_metadata, df_additional):
    df.columns = df.columns.str.strip()

df_merged = (
    df_data
      .merge(df_metadata,   on=["Country", "Year"], how="left")
      .merge(df_additional, on=["Country", "Year"], how="left")
)
Code
df1 = pd.read_csv('Quarto-Assignment/unicef_data.csv').rename(columns={
    'Country ': 'country',
    'Year': 'year',
    'Mortality Rate per 1,000 infants aged 1 month': 'infant_mortality_rate'
})
df1['year'] = df1['year'].astype(int)
df1['country'] = df1['country'].str.strip()

countries = sorted(df1['country'].unique())
years = list(range(1980, 2023))
full_index = list(itertools.product(countries, years))
full_df = pd.DataFrame(full_index, columns=['country','year'])

full_df = full_df.merge(
    df1[['country','year','infant_mortality_rate']],
    on=['country','year'], how='left'
)

import plotly.express as px

fig = px.choropleth(
    full_df,
    locations='country',
    locationmode='country names',
    color='infant_mortality_rate',
    hover_name='country',
    animation_frame='year',
    animation_group='country',
    projection='natural earth',
    category_orders={'year': years},
    title='<b><u>Infant Mortality Rate Over Time (1980–2022)</u></b>',
    color_continuous_scale=px.colors.sequential.Reds,
    labels={'infant_mortality_rate': 'Infant Mortality (per 1 000)'}
)

fig.update_layout(
    title_x=0.5,                    # center the title
    title_font=dict(                # adjust font settings if you like
        size=24,
        family="Arial, sans-serif",
        color="#004080"
    )
)

# Style map: ocean and land colors
fig.update_geos(
    showocean=True,
    oceancolor='rgb(0, 105, 148)',
    showland=True,
    landcolor='white'
)

# Colorbar title vertical with dynamic scaling per frame
def_colorbar = dict(title='Deaths per 1,000 Infants', title_side='right')
fig.update_layout(
    height=700,
    width=1100,
    margin=dict(l=0, r=0, t=50, b=0),
    coloraxis_colorbar=def_colorbar
)

# Animation speed
if fig.layout.updatemenus:
    btn = fig.layout.updatemenus[0].buttons[0]
    btn.args[1]['frame']['duration'] = 500
    btn.args[1]['transition']['duration'] = 300

fig.show()

A Global Journey in Shades of Red: Infant Mortality (1980–2022)

Today’s Snapshot (2022)

On the map’s final frame—2022—we confront a world of progress and persistence:

  • Niger (≈86 ‰): Still the darkest red, reflecting entrenched malnutrition and fragile health services that leave one in twelve newborns at risk of not surviving their first month.
  • Somalia (≈82 ‰) & Chad (≈75 ‰): Conflict and displacement perpetuate high mortality despite modest economic gains—showing how stability is as crucial as wealth.
  • Central African Republic (≈73 ‰) & Sierra Leone (≈71 ‰): Emerging from crises yet trapped in a high-mortality band, underscoring that recovery must be paired with sustained health-system rebuilds.
  • South Asia’s Hotspots: Afghanistan (~65 ‰) and Pakistan (~55 ‰) remain deeper shades of red than their regional peers, highlighting gaps in vaccination coverage and maternal care.
  • Bright Spots: Rwanda (~25 ‰), Bangladesh (~22 ‰), and Vietnam (~14 ‰) stand out in pastel hues, emblematic of how targeted immunization drives and primary-care networks can rewrite national trajectories.

1980 vs. 2022: Dramatic Shifts

Code
import pandas as pd
import requests
from io import BytesIO
from PIL import Image
import pycountry
import plotly.graph_objects as go

df = pd.read_csv("Quarto-Assignment/unicef_additional.csv")
df.columns = df.columns.str.strip()
df["Country"] = df["Country"].str.strip()
df2022 = (
    df[(df.Year == 2022) & (df.Gender == "Total")]
      [["Country", "Mortality rate per 1,000 children aged 5"]]
      .rename(columns={"Mortality rate per 1,000 children aged 5": "Mortality5"})
)

top20 = (
    df2022.nlargest(20, "Mortality5")
           .sort_values("Mortality5", ascending=True)
           .reset_index(drop=True)
)

# 2) Load metadata for birth rate crude & life expectancy
meta = pd.read_csv("Quarto-Assignment/unicef_metadata.csv")
meta.columns = meta.columns.str.strip()
meta["Country"] = meta["Country"].str.strip()
meta22 = meta[meta.Year == 2022].copy()
# Convert birth rate crude and life expectancy to numeric
meta22.loc[:, "BirthRate"] = pd.to_numeric(meta22["Birth rate, crude (per 1,000 people)"], errors="coerce")
meta22.loc[:, "LifeExp"] = pd.to_numeric(meta22["Life expectancy at birth, total (years)"], errors="coerce")

# Merge metadata into top20 on Country
top20 = top20.merge(
    meta22[["Country", "BirthRate", "LifeExp"]],
    on="Country",
    how="left"
)

# 3) ISO2 lookup & sample flag color for bars
def iso2(name):
    try:
        return pycountry.countries.lookup(name).alpha_2.lower()
    except:
        return None


def sample_flag_color(code):
    url = f"https://flagcdn.com/40x30/{code}.png"
    img = Image.open(BytesIO(requests.get(url).content)).convert("RGBA")
    r, g, b, _ = img.resize((1,1), Image.LANCZOS).getpixel((0,0))
    return f"#{r:02x}{g:02x}{b:02x}"

colors, flags = [], []
for c in top20["Country"]:
    code2 = iso2(c)
    if code2:
        colors.append(sample_flag_color(code2))
        flags.append(f"https://flagcdn.com/20x15/{code2}.png")
    else:
        colors.append("lightgray")
        flags.append(None)

top20["color"] = colors
top20["flag_url"] = flags

# 4) Plotly bar chart with birth rate & life expectancy in hover
fig = go.Figure(
    go.Bar(
        x=top20["Mortality5"],
        y=top20["Country"],
        orientation="h",
        marker_color=top20["color"],
        customdata=top20[["BirthRate", "LifeExp"]],
        hovertemplate=(
            "<b>%{y}</b><br>"
            "Mortality: %{x:.1f} per 1,000<br>"
            "Birth rate: %{customdata[0]:.1f} per 1,000<br>"
            "Life exp: %{customdata[1]:.1f} yrs<br>"
            "<extra></extra>"
        )
    )
)

# 5) Overlay mini-flags
images = []
for _, row in top20.iterrows():
    if row.flag_url:
        images.append(dict(
            source=row.flag_url,
            xref="x", yref="y",
            x=row["Mortality5"] + 2,
            y=row["Country"],
            xanchor="left", yanchor="middle",
            sizex=3, sizey=0.4
        ))
fig.update_layout(images=images)

max_val = top20["Mortality5"].max()
fig.update_layout(
    title='<b><u>Countries With The Highest Rate of Child Mortality (5 Years of Age)</u></b>',
    title_x=0.5,
    title_font=dict(
        size=24,
        family="Arial, sans-serif",
        color="#004080"
    ),
    xaxis_title="Deaths per 1,000 Children at Age 5",
    yaxis_title="Country",
    template="simple_white",
    width=900,
    height=600
)
fig.update_xaxes(range=[0, max_val * 1.15])

fig.show()

The Hardest Places to Reach Age 5 in 2022

<h2 style=“color:#007ACC; margin-bottom:0.3em;

While neonatal mortality often commands the spotlight, death before a fifth birthday remains a critical indicator of sustained health system performance, nutrition, and social stability. This analysis presents the 20 countries with the highest child-5 mortality rates in 2022, interrogating not only the raw figures but the underlying drivers and regional patterns.

Key Findings

  1. Highest Burden Concentration:
    Somalia (74.3 ‰) and South Sudan (71.2 ‰) reflect protracted conflict, displacement, and weakened health systems.
    Sierra Leone (65.7 ‰), Niger (64.9 ‰), and the Central African Republic (63.5 ‰) underscore mortality5 entrenched in regions beset by instability and food insecurity.
  2. Regional Clustering:
    West & Central Africa account for 9 of the top 10 countries—highlighting shared challenges of underinvestment, climate shocks, and political fragility.
  3. Notable Divergences:
    Chad (60.2 ‰) and Niger show that community midwifery and modest program gains can begin to decelerate mortality5 even under resource constraints.
    Emerging Successes: Rwanda (≈25 ‰) and Uganda (≈42 ‰) have halved mortality5 since 2012 through vaccination, growth-monitoring, and decentralized care.

High child-5 mortality indicates gaps in post-neonatal interventions—nutrition supplementation, malaria prophylaxis, and early childhood development programs—often failing to reach the most vulnerable. Gender and equity considerations further compound risk, as impoverished contexts can disproportionately limit girls’ access to care and education.

Policy Implications:
1. Integrated Programming: Align WASH, nutrition, and immunization under a unified framework spanning “First 1,000 Days” through “Second 1,000 Days.”
2. Conflict-Sensitive Delivery: In Somalia and South Sudan, leverage local NGOs and mobile health units to maintain continuity of care.
3. Data-Driven Adaptation: Employ rapid household surveys to detect mortality spikes from droughts or outbreaks and deploy rapid-response task forces.

Child-5 mortality remains alarmingly high in a concentrated set of countries. The stark contrast between upper echelon nations (Somalia, South Sudan) and near-success stories (Rwanda, Uganda) affirms that targeted, context-specific interventions can dramatically alter outcomes. Realizing SDG 3.2—ending preventable deaths of children under 5—will demand both scaling proven interventions and innovating delivery in fragile, high-burden settings.

“Reaching age five should be the norm, not the exception. This chart signals where the world must intensify efforts to make survival not a privilege of geography, but a universal right.”

Data source: UNICEF 2022 child-5 mortality.

Code
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from sklearn.linear_model import LinearRegression
import numpy as np

mort = pd.read_csv("Quarto-Assignment/unicef_data.csv")
meta = pd.read_csv("Quarto-Assignment/unicef_metadata.csv")

mort.columns = mort.columns.str.strip()
meta.columns = meta.columns.str.strip()

mort2022 = mort[mort["Year"] == 2022].copy()
mort2022.rename(columns={"Mortality Rate per 1,000 infants aged 1 month": "Mortality"}, inplace=True)

meta2022 = meta[meta["Year"] == 2022].copy()
meta2022["GDP_pc"] = (
    meta2022["GDP per capita (constant 2015 US$)"]
    .str.replace(r"[\$,]", "", regex=True)
    .astype(float)
)
meta2022["Population"] = meta2022["Population, total"].astype(float)

# African countries list
african_countries = [
    "Algeria", "Angola", "Benin", "Botswana", "Burkina Faso", "Burundi",
    "Cabo Verde", "Cameroon", "Central African Republic", "Chad", "Comoros",
    "Congo, the Democratic Republic of the", "Congo", "Cote d'Ivoire",
    "Djibouti", "Egypt", "Equatorial Guinea", "Eritrea", "Eswatini",
    "Ethiopia", "Gabon", "Gambia", "Ghana", "Guinea", "Guinea-Bissau", "Kenya",
    "Lesotho", "Liberia", "Libya", "Madagascar", "Malawi", "Mali", "Mauritania",
    "Mauritius", "Morocco", "Mozambique", "Namibia", "Niger", "Nigeria", "Rwanda",
    "Sao Tome and Principe", "Senegal", "Seychelles", "Sierra Leone", "Somalia",
    "South Africa", "South Sudan", "Sudan", "Tanzania", "Togo", "Tunisia", "Uganda",
    "Zambia", "Zimbabwe"
]

# Filter datasets to African countries
df = pd.merge(
    mort2022[mort2022["Country"].isin(african_countries)][["Country", "Mortality"]],
    meta2022[meta2022["Country"].isin(african_countries)][["Country", "GDP_pc", "Population"]],
    on="Country"
).dropna()

# Create interactive bubble scatter with color mapped to Mortality
fig = px.scatter(
    df,
    x="GDP_pc",
    y="Mortality",
    size="Population",
    color="Mortality",
    color_continuous_scale="Reds",
    size_max=60,
    hover_name="Country",
    hover_data={"GDP_pc": ":.0f", "Mortality": ":.1f", "Population": ":.0f"},
    labels={"GDP_pc": "GDP per Capita (2015 US$)", "Mortality": "Infant Mortality (per 1 000)"},
    title="Infant Mortality vs. GDP per Capita for African Countries (2022)",
)

fig.update_xaxes(type="log")

# Fit OLS regression on log(GDP) → Mortality
X = np.log(df["GDP_pc"].values).reshape(-1, 1)
y = df["Mortality"].values
model = LinearRegression().fit(X, y)
x_range = np.linspace(X.min(), X.max(), 100)
y_pred = model.predict(x_range.reshape(-1, 1))

# Compute 95% CI
resid = y - model.predict(X)
sigma = resid.std(ddof=1)
ci = 1.96 * sigma
upper = y_pred + ci
lower = y_pred - ci

# 9) Overlay regression line and confidence band
fig.add_trace(go.Scatter(
    x=np.exp(x_range),
    y=y_pred,
    mode="lines",
    name="Trendline",
    line=dict(color="black", dash="dash")
))
fig.add_trace(go.Scatter(
    x=np.concatenate([np.exp(x_range), np.exp(x_range)[::-1]]),
    y=np.concatenate([upper, lower[::-1]]),
    fill="toself",
    fillcolor="rgba(0,0,0,0.1)",
    line=dict(color="rgba(255,255,255,0)"),
    hoverinfo="skip",
    showlegend=False,
))

fig.update_layout(
    title='<b><u>Infant Mortality vs. GDP per Capita for African Countries (2022)</u></b>',
    title_x=0.5,
    title_font=dict(
        size=24,
        family="Arial, sans-serif",
        color="#004080"
    ),
    template="simple_white",
    width=900,
    height=550,
    yaxis_title="Infant Mortality (per 1 000)",
    legend=dict(title="", yanchor="top", y=0.99, xanchor="left", x=0.01)
)

fig.show()

Infant Mortality vs. GDP per Capita Across Africa (2022)

This interactive bubble chart explores how economic prosperity relates to infant survival across African nations:

  • X-axis (log scale): GDP per Capita (constant 2015 US$)
  • Y-axis: Infant Mortality Rate (deaths per 1 000 infants at 1 month)
  • Bubble size: Country population (larger bubbles = more people)
  • Bubble color (red scale): Mortality rate (darker = higher mortality)
  • Trendline: Ordinary Least Squares regression on log(GDP) → mortality, with a semi-transparent 95 % confidence band

Spotlight on Key Nations

  • Niger (GDP ≈ $1 200): Sits above the trendline with ~86 ‰ mortality—extreme poverty and health-system gaps intersect.
  • Chad (GDP ≈ $1 600): With ~75 ‰ mortality, illustrates how conflict and infrastructure deficits stall survival gains.
  • Somalia (GDP ≈ $500): Deep red, small bubble (~82 ‰) highlights instability, malnutrition, and weak services.
  • Ghana (GDP ≈ $2 400): Outperforms peers with ~22 ‰ mortality, showcasing effective health investments.
  • Angola (GDP ≈ $4 500): Despite wealth, ~60 ‰ mortality underscores gaps in equitable care.

Why It Matters

The steep downward slope confirms that wealth generally saves lives, but outliers—Niger, Chad, and Somalia—remind us that stability, infrastructure, and targeted policies are equally critical.

How to Explore

  • Hover on any bubble to reveal country name, exact GDP per capita, precise mortality rate, and population.
  • Zoom & pan to compare clusters—contrast fragile states with rising economies or peer nations side by side.

Data sources: UNICEF 2022 infant mortality; UNICEF 2022 GDP per capita & population metadata

Code
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

df = pd.read_csv("Quarto-Assignment/unicef_additional.csv")
df.columns = df.columns.str.strip()
df_tot = df[(df["Year"] == 2022) & (df["Gender"] == "Total")].copy()
df_tot = df_tot[["Country", "Mortality rate per 1,000 children aged 5"]]

df_top20 = (
    df_tot
    .sort_values("Mortality rate per 1,000 children aged 5", ascending=False)
    .head(20)
)

# Military expenditure data
meta = pd.read_csv("Quarto-Assignment/unicef_metadata.csv")
meta.columns = meta.columns.str.strip()
meta_2022 = meta[meta["Year"] == 2022].copy()
meta_2022["Mil_Exp_pctGDP"] = pd.to_numeric(
    meta_2022["Military expenditure (% of GDP)"], errors="coerce"
)

# Merge for the same 20 countries
df_merge = pd.merge(
    df_top20,
    meta_2022[["Country", "Mil_Exp_pctGDP"]],
    on="Country",
    how="left"
).dropna()

df_merge = df_merge.sort_values(
    "Mortality rate per 1,000 children aged 5", ascending=False
)

fig = make_subplots(specs=[[{"secondary_y": True}]])

fig.add_trace(
    go.Bar(
        x=df_merge["Country"],
        y=df_merge["Mortality rate per 1,000 children aged 5"],
        name="Child Mortality (per 1 000 at age 5)",
        marker_color="indianred"
    ),
    secondary_y=False
)

fig.add_trace(
    go.Scatter(
        x=df_merge["Country"],
        y=df_merge["Mil_Exp_pctGDP"],
        name="Military Expenditure (% of GDP)",
        mode="lines+markers",
        marker=dict(color="darkblue")
    ),
    secondary_y=True
)

fig.update_layout(
    title='<b><u>Top 20 Countries by Child-5 Mortality vs. Military Spending (2022)</u></b>',
    title_x=0.5,
    title_font=dict(
        size=24,
        family="Arial, sans-serif",
        color="#004080"
    ),
    xaxis_tickangle=-45,
    template="simple_white",
    width=900,
    height=500,
)

fig.update_yaxes(
    title_text="Mortality rate per 1 000 children (age 5)",
    secondary_y=False
)
fig.update_yaxes(
    title_text="Military Expenditure (% of GDP)",
    secondary_y=True
)

fig.show()

Child Mortality (5 years of age or less) vs. Military Spending (2022)

The dual-axis chart uncovers key insights into how child mortality and defense budgets intersect among the 20 worst-affected countries:

  1. Niger exhibits the highest child mortality rate, exceeding 200 deaths per 1 000 infants, while its military spending remains relatively low, underscoring a dire health crisis unaddressed by defense allocations.
  2. South Sudan boosted its military spedning to $0.38 billion in 2022—a 77.68 % increase from 2021, despite its high child mortality rate of 58.8 per 1,000 children.
  3. Countries with lower military spending, such as Zambia and Benin, generally record lower mortality rates, suggesting that redirecting resources from defense to healthcare could significantly improve outcomes.
  4. The chart suggests an inverse relationship in several cases, where higher military expenditure does not correlate with better health, highlighting potential misallocation of resources.

These findings emphasize the urgent need for policymakers to shift budget priorities toward health infrastructure—such as hospitals, clinics, and nutrition programs—to drive down child mortality.

Code
import pandas as pd
import plotly.graph_objects as go

# Load your data
df_m = pd.read_csv("Quarto-Assignment/unicef_additional.csv", skipinitialspace=True)
df_m.columns = df_m.columns.str.strip()

meta = pd.read_csv("Quarto-Assignment/unicef_metadata.csv", skipinitialspace=True)
meta.columns = meta.columns.str.strip()

countries = [
    "Nepal", "Niger", "Afghanistan", "Mali", "Madagascar", "Yemen",
    "Ivory Coast", "Mauritania", "Benin", "Chad", "Pakistan",
    "Senegal", "Bangladesh", "Guinea"
]

# Mortality
mort2022 = df_m[
    (df_m["Year"] == 2022) &
    (df_m["Country"].isin(countries))
][["Country", "Mortality rate per 1,000 children aged 5"]].rename(
    columns={"Mortality rate per 1,000 children aged 5": "Mortality5"}
)

# Hospital beds
beds = meta[meta["Country"].isin(countries)][["Country", "Year", "Hospital beds (per 1,000 people)"]]
beds["Beds_per_1000"] = pd.to_numeric(beds["Hospital beds (per 1,000 people)"], errors="coerce")
beds_latest = beds.dropna(subset=["Beds_per_1000"]).sort_values("Year").groupby("Country").tail(1)

# Merge
df = mort2022.merge(
    beds_latest[["Country", "Beds_per_1000", "Year"]],
    on="Country", how="left"
)
df = df.sort_values("Beds_per_1000", ascending=False).reset_index(drop=True)

# Make mortality negative
df["Mortality5_negative"] = -df["Mortality5"]

# Create a single figure
fig = go.Figure()

# Add Mortality Bars (left) - Semi-transparent
fig.add_trace(go.Bar(
    x=df["Mortality5_negative"], 
    y=df["Country"],
    orientation="h",
    name="Child Mortality",
    marker=dict(color="tomato", opacity=0.7),  # semi-transparent
    hovertemplate="Deaths per 1 000 (2022): %{customdata:.1f}<extra></extra>",
    customdata=df["Mortality5"]
))

# Add Hospital Beds Bars (right) - Semi-transparent
fig.add_trace(go.Bar(
    x=df["Beds_per_1000"], 
    y=df["Country"],
    orientation="h",
    name="Hospital Beds",
    marker=dict(color="steelblue", opacity=0.7),  # semi-transparent
    hovertemplate="Beds per 1 000 (Year %{customdata[0]}): %{x:.2f}<extra></extra>",
    customdata=df[["Year"]]
))

# Layout tweaks
fig.update_layout(
    template="simple_white",
    barmode='overlay',  # Overlay bars neatly
    width=950, height=650,
    margin=dict(t=100, l=100, r=100, b=50),
    title=dict(
        text='<b><u>Butterfly Chart: Child Mortality Rate vs. Hospital Beds</u></b>',
        x=0.5,
        xanchor='center',
        font=dict(size=26, family="Arial, sans-serif", color="#004080"),
    ),
    xaxis=dict(
        title="Rates per 1,000",
        zeroline=True,
        zerolinewidth=2,
        zerolinecolor='black',
        tickfont=dict(size=12)
    ),
    yaxis=dict(
        title="",
        autorange="reversed",
        tickfont=dict(size=12)
    ),
    legend=dict(
        orientation="h",
        yanchor="bottom",
        y=1.02,
        xanchor="center",
        x=0.5,
        font=dict(size=14)
    )
)

# Add a zero vertical line manually for clarity
fig.add_vline(x=0, line_width=2, line_color="black")

# Show the figure
fig.show()

📊 Butterfly Chart: Child Mortality vs. Hospital Beds per 1,000

This butterfly chart compares:

  • Left Side: Child mortality rates (children under 5 years)
  • Right Side: Number of hospital beds per 1,000 people

It highlights the healthcare challenges faced by some of the world’s poorest countries.


📈 Key Observations

  • ⚠️ The countries listed above have fewer than 1 hospital bed per 1,000 people.
  • 📉 Child mortality rates are extremely high, often above 50 deaths per 1,000 live births.
  • 🏥 Hospital infrastructure is critically limited, indicating major barriers to accessing medical care.
  • 🌍 Countries like Niger, Chad, and Mauritania show particularly severe gaps between healthcare access and mortality outcomes.

🧠 This Indicates

  • Severe Health Access Gap: Limited hospital beds mean many families cannot access timely healthcare services, even in emergencies.
  • Underdeveloped Health Systems: Reflects chronic underinvestment in public health infrastructure and workforce.
  • Preventable Deaths: High mortality rates highlight the preventable nature of many child deaths with basic interventions.
  • Cycle of Poverty and Poor Health: Weak healthcare systems contribute to ongoing cycles of poverty, low productivity, and poor human development outcomes.

This chart illustrates that without substantial investment in basic healthcare infrastructure - particularly hospitals and clinics - improving child survival rates in the poorest countries will remain a major global challenge.

Efforts to achieve Sustainable Development Goal 3 (Good Health and Well-Being) must prioritize:

  • Expanding hospital capacity,
  • Training and retaining healthcare workers,
  • Ensuring equitable access to essential services.

Code
import pandas as pd
import plotly.graph_objects as go
import numpy as np

# Load & clean data
df = pd.read_csv('Quarto-Assignment/unicef_data.csv')
meta = pd.read_csv('Quarto-Assignment/unicef_metadata.csv')

df['Country'] = df['Country '].str.strip().replace('United States of America', 'United States')
meta['Country'] = meta['Country '].str.strip().replace('United States of America', 'United States')

# Define G7
G7 = ['Canada', 'France', 'Germany', 'Italy', 'Japan', 'United Kingdom', 'United States']

# Filter mortality data
mort = (
    df[df['Country'].isin(G7) & df['Year'].between(2012, 2022)]
    [['Country', 'Year', 'Mortality Rate per 1,000 infants aged 1 month']]
    .rename(columns={'Mortality Rate per 1,000 infants aged 1 month': 'Mortality'})
)

# Filter GDP data
meta_sub = meta[meta['Year'].between(2012, 2022)].copy()
meta_sub['GDP_pc'] = (
    meta_sub['GDP per capita (constant 2015 US$)']
    .replace(r'[\$,]', '', regex=True)
    .astype(float)
)
gdp = meta_sub[meta_sub['Country'].isin(G7)][['Country', 'Year', 'GDP_pc']]

# Merge and pivot
merged = mort.merge(gdp, on=['Country', 'Year'], how='inner')
years = list(range(2012, 2023))

mort_mat = (
    merged.pivot(index='Country', columns='Year', values='Mortality')
    .reindex(index=G7, columns=years)
)
gdp_mat = (
    merged.pivot(index='Country', columns='Year', values='GDP_pc')
    .reindex(index=G7, columns=years)
)

# Build heatmap with dual hover info
custom = np.dstack([mort_mat.values, gdp_mat.values])
fig = go.Figure(go.Heatmap(
    z=mort_mat.values,
    x=years,
    y=G7,
    customdata=custom,
    colorscale='Turbo',
    colorbar=dict(title='‰ Mortality'),
    hovertemplate=(
        "Country: %{y}<br>"
        "Year: %{x}<br>"
        "Mortality: %{z:.1f} ‰<br>"
        "GDP per Capita: $%{customdata[1]:,.0f}<extra></extra>"
    )
))

# Update layout
fig.update_layout(
    template="simple_white",
    height=650,      # <<< Increased height
    width=950,       # <<< Increased width
    title=dict(
        text='<b><u>Infant Mortality & GDP per Capita (2012–2022) — G7 Comparison</u></b>',
        x=0.5,
        xanchor='center',
        font=dict(size=24, family="Arial, sans-serif", color="#004080"),  # Slightly larger title
        pad=dict(t=20, b=10)
    ),
    xaxis_title="Year",
    yaxis_title="Country",
    margin=dict(l=80, r=80, t=180, b=80)  # <<< More top margin, no autoexpand
)

fig.show()

G7 Must Cancel the Debt of the World’s Poorest Nations

While G7 countries enjoy some of the lowest infant mortality rates on earth—often under 5 deaths per 1,000 live births—many low-income nations continue to suffer infant and child mortality rates above 80 per 1,000.
These disparities are rooted not only in healthcare access but in the crushing burden of sovereign debt, which diverts scarce government resources away from hospitals, clinics, and basic public health programs.

By forgiving or restructuring the illicit and unsustainable debts of the world’s poorest countries, G7 governments could unlock billions in fiscal space for:

  • ▪️ Expanding maternal and neonatal care facilities
  • ▪️ Hiring and training community health workers
  • ▪️ Scaling up immunization and nutrition programs
  • ▪️ Investing in clean water, sanitation, and emergency response

Debt cancellation isn’t charity—it’s a strategic investment in global health security and human rights. As leaders of the world’s largest economies, G7 nations have both the capacity and the moral imperative to ensure that no child’s chance at life is mortgaged to external creditors.

Code
import pandas as pd
import numpy as np
import plotly.graph_objects as go

# 1) Load data and clean
df = pd.read_csv('Quarto-Assignment/unicef_data.csv')
df.columns = df.columns.str.strip()

# 2) Calculate yearly average mortality
ts = (
    df
    .groupby("Year")["Mortality Rate per 1,000 infants aged 1 month"]
    .mean()
    .reset_index(name="Avg_Mortality")
)

# 3) Fit an OLS trend to the historical data
coef = np.polyfit(ts["Year"], ts["Avg_Mortality"], 1)

# 4) Create an extended year range through 2030
year_min, year_max = ts["Year"].min(), 2030
years_ext = np.arange(year_min, year_max + 1)
trend_ext = np.polyval(coef, years_ext)

# 5) Separate past vs. projection
mask_past = years_ext <= ts["Year"].max()
mask_proj = years_ext > ts["Year"].max()

# 6) Build the figure
fig = go.Figure()

# Actual data line + markers
fig.add_trace(go.Scatter(
    x=ts["Year"],
    y=ts["Avg_Mortality"],
    mode="lines+markers",
    name="Avg Mortality (1980–2022)",
    line=dict(color="steelblue", width=2),
    marker=dict(size=6),
    hovertemplate="Year: %{x}<br>Rate: %{y:.1f} per 1 000<extra></extra>"
))

# Fitted OLS trend on historical data
fig.add_trace(go.Scatter(
    x=years_ext[mask_past],
    y=trend_ext[mask_past],
    mode="lines",
    name="Trend Fit (OLS)",
    line=dict(color="gray", dash="dash"),
    hoverinfo="skip"
))

# Projected extension to 2030
fig.add_trace(go.Scatter(
    x=years_ext[mask_proj],
    y=trend_ext[mask_proj],
    mode="lines",
    name="Projection to 2030",
    line=dict(color="gray", dash="dot"),
    hovertemplate="Year: %{x}<br>Proj: %{y:.1f} per 1 000<extra></extra>"
))

# 7) Layout with perfect sizing
fig.update_layout(
    title=dict(
        text='<b><u>Global Average Infant Mortality Rate (1980–2022) with Projection to 2030</u></b>',
        x=0.5,
        xanchor='center',
        font=dict(size=24, family="Arial, sans-serif", color="#004080"),
        pad=dict(t=10, b=10)
    ),
    xaxis=dict(
        title="Year",
        rangeslider=dict(visible=True),
        rangeselector=dict(
            buttons=[
                dict(step="all", label="All"),
                dict(count=10, step="year", stepmode="backward", label="10 Y"),
                dict(count=5, step="year", stepmode="backward", label="5 Y"),
                dict(count=1, step="year", stepmode="backward", label="1 Y"),
            ]
        ),
        tickmode='linear',
        tick0=1980,
        dtick=5
    ),
    yaxis=dict(
        title="Deaths per 1,000 Infants",
        rangemode="tozero"
    ),
    template="plotly_white",
    height=650,  # <<< More height!
    width=1000,  # <<< More width for cleaner publication
    hovermode="x unified",
    margin=dict(l=80, r=80, t=180, b=80)  # <<< Bigger margins to avoid any cut-off
)

fig.show()

Figure: Global Average Infant Mortality Rate (1980–2022) with Projection to 2030


0.1 1. Sustained Long-Term Decline

“From ≈90 deaths per 1,000 in 1980 to ≈14 in 2022…”
Over the past four decades, the world has seen a remarkable and nearly continuous decrease in infant mortality—averaging 1.66 fewer deaths per 1,000 infants each year. This steady improvement underscores the cumulative impact of:
- Expanded vaccination programs
- Enhanced maternal and neonatal care
- Targeted nutrition and sanitation campaigns


0.2 2. Acceleration & Plateaus

  • 1980s–1990s:
    The steepest annual drops (>1.7 deaths/year) coincide with the rollout of Oral Rehydration Therapy and measles immunization.
  • 2000s–2010s:
    Declines continue (~1.4–1.6 deaths/year) but demand tackling preterm complications and regional disparities.
  • Post-2015 Plateau:
    Sub-0.5 fewer deaths/year in some recent years suggests diminishing returns—the “last mile” is hardest.*

0.3 3. Projection to 2030: A Cautious Outlook

Trend Equation: y = –1.66 × Year + 3,440
2030 Forecast:8 deaths per 1,000 infants
If we simply extrapolate the historical OLS trend (dotted line), we approach—but may not fully meet—the SDG target of ≤12 deaths per 1,000 by 2030. Progress is real, yet the window for under-performance narrows.


0.4 4. Policy Implications & Next Steps

  • 🎯 Target the “Last Mile”: Remote, conflict-affected, and underserved urban pockets.
  • 📊 Data-Driven Equity: District-level dashboards and real-time monitoring for agile resource allocation.
  • 🔬 Innovation in Neonatal Care: Affordable monitoring devices, point-of-care diagnostics, tele-medicine networks.

“Every line on this chart represents real lives saved—and still, untold potential remains.”